package tcp

import (
	"bufio"
	"context"
	"gitee.com/easy_code/go-redis/lib/logger"
	"gitee.com/easy_code/go-redis/lib/sync/atomic"
	"gitee.com/easy_code/go-redis/lib/sync/wait"
	"io"
	"net"
	"sync"
	"time"
)

/**
业务引擎
*/

// goland 使用技巧：鼠标放在 EchoHandler 上，然后键盘 ctr + i，输入要实现的接口，即可自动生成接口对应方法

// EchoClient 记录客户端信息（一个 EchoClient 代表一个客户端）
// 实现 closer 接口，统一关闭的方法
type EchoClient struct {
	Conn    net.Conn
	Waiting wait.Wait
}

func MakeHandler() *EchoHandler {
	return &EchoHandler{}
}

func (e *EchoClient) Close() error {
	// 为了防止还有执行中的程序，最多等待10秒
	e.Waiting.WaitWithTimeout(10 * time.Second)
	_ = e.Conn.Close()
	return nil
}

type EchoHandler struct {
	// 记录活跃的连接
	activeConn sync.Map
	// 记录是否关闭中，关闭中就不接受信息
	closing atomic.Boolean
}

func (handler *EchoHandler) Handle(ctx context.Context, conn net.Conn) {
	// 如果正在关闭中，不接受其他连接
	if handler.closing.Get() {
		_ = conn.Close()
	}

	// 将新连接包装成 EchoClient
	client := &EchoClient{
		Conn: conn,
	}
	// 记录连接这个服务的客户端
	handler.activeConn.Store(client, struct{}{})
	// 接收用户传输过来的信息
	reader := bufio.NewReader(conn)
	for {
		msg, err := reader.ReadString('\n') // 规定数据以 \n 结尾
		if err != nil {
			if err == io.EOF {
				logger.Info("Connection close")
				handler.activeConn.Delete(client)
			} else {
				logger.Warn(err)
			}
			return
		}

		// 防止执行中的程序突然关闭
		client.Waiting.Add(1)
		b := []byte(msg)
		_, _ = conn.Write(b)
		client.Waiting.Done()
	}
}

func (handler *EchoHandler) Close() error {
	logger.Info("handler shutting down")
	// 将服务状态改为：关闭中
	handler.closing.Set(true)
	// Range 回调参数返回值 bool 表示是否需要操作下一个 key
	handler.activeConn.Range(func(key, value interface{}) bool {
		// 将 key 转换成 EchoClient
		client := key.(*EchoClient)
		_ = client.Conn.Close()
		return true
	})
	return nil
}
